home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 August: Tool Chest / Dev.CD Aug 94.toast / Tool Chest / Networking & Communications / MacTCP / MacTCP Developer Tools / HyperCard MacTCP Toolkit 1.0 / Source Code ƒ / orderMessages.p < prev    next >
Encoding:
Text File  |  1993-06-15  |  11.3 KB  |  379 lines  |  [TEXT/MPS ]

  1. (*
  2.     orderMessages(string,group,checkMsgs) -- View the string as a series of subject lines from mail messages,
  3.         with the message number preceeding each line. Move the message numbers to the end of each line, surrounded
  4.         by international quotes («...»). Sort the lines by message number and remove duplicates. If the second
  5.         parameter is "true", group together lines which are the same, ignoring initial "Re: "s. Take the third
  6.         parameter as a sparse list (a list of items, each of which is a number or an interval specified by two numbers,
  7.         low and high); for each message number in the list, put a check-mark in front of the corresponding message
  8.         line.
  9.  
  10.     Note: This XCMD was developed specifically for the HyperNetNews stack, and is probably not very useful with
  11.         any other stack. It is not really intended to provide any generally useful functionality.
  12.  
  13.     To compile and link this file using Macintosh Programmers Workshop,
  14.  
  15.         pascal -w orderMessages.p
  16.         link -m ENTRYPOINT -o HyperCommands -rt XFCN=7869 -sn Main=orderMessages ∂
  17.             orderMessages.p.o "{Libraries}HyperXLib.o" "{MPW}"Libraries:interface.o "{MPW}"Libraries:PLibraries:PasLib.o
  18.  
  19.     © Copyright 1989 by Apple Computer, Inc.
  20.  
  21.     Initial coding 2/15/89 by Harry R. Chesley.
  22. *)
  23.  
  24. {$R-}
  25.  
  26. {$S orderMessages }     { Segment name must be the same as the command name. }
  27.  
  28. unit DummyUnit;
  29.  
  30. interface
  31.  
  32. uses MemTypes, QuickDraw, OSIntf, ToolIntf, HyperXCmd;
  33.  
  34. procedure EntryPoint(paramPtr: XCmdPtr);
  35.     
  36. implementation
  37.  
  38. const
  39.  
  40. RETURN = 13;                { ASCII for carriage return. }
  41.  
  42. procedure orderMessages(paramPtr: XCmdPtr); forward;
  43.  
  44. procedure EntryPoint(paramPtr: XCmdPtr);
  45.  
  46.     begin
  47.         orderMessages(paramPtr);
  48.     end;
  49.  
  50. procedure orderMessages(paramPtr: XCmdPtr);
  51.  
  52.     type
  53.     
  54.     lineRec =
  55.         record
  56.             numberStart: Ptr;        { Start of number text. }
  57.             textStart: Ptr;                { Start of main subject line text. }
  58.             compareStart: Ptr;        { Place to start comparing lines (after number and "re: "). }
  59.             msgNumber: LongInt;    { The actual message number. }
  60.             checkMark: boolean;        { True if we're to put a check-mark before the line on output. }
  61.             indent: boolean;            { True if we're to indent the line on output. }
  62.         end;
  63.  
  64.     lineRecArray = array [1..30000] of lineRec;
  65.  
  66.     lineRecPtr = ^lineRecArray;
  67.     lineRecHand = ^lineRecPtr;
  68.  
  69.     nextElement = (endOfString, newItem, newWord);
  70.  
  71.     var str: Str255;
  72.         newEntry: lineRec;                { The latest input. }
  73.         groupMessages: boolean;        { True if we should group subjects together. }
  74.         sourceHand: Handle;                { Handle to the original source text. }
  75.         checkPtr: Ptr;                        { Pointer into checkMsgs parameter. }
  76.         loCheck: LongInt;                    { Low number in range from checkMsgs item. }
  77.         highCheck: LongInt;                { High number in checkMsgs item range. }
  78.         resultHand: Handle;                { Handle to the result text. }
  79.         resultSize: longInt;                { Size of the result. }
  80.         linePtrs: lineRecHand;            { Handle to the line pointers. }
  81.         lineCount: integer;                { Number of lines in the source. }
  82.         tempLine: lineRec;
  83.         p, p2: Ptr;
  84.         i, j, k, delta: integer;
  85.         dupEntry: boolean;
  86.  
  87.     procedure Fail(errMsg: Str255); { set theResult and quit }
  88.         begin
  89.             paramPtr^.returnValue := PasToZero(paramPtr,errMsg);
  90.             exit(orderMessages);
  91.         end;
  92.  
  93.     procedure disposAndFail(errMsg: Str255);
  94.         begin
  95.             HUnlock(sourceHand);
  96.             disposHandle(Handle(linePtrs));
  97.             Fail(errMsg);
  98.         end;
  99.  
  100.     procedure putByte(b: SignedByte);
  101.         { Store a byte into the location pointed to by p and increment p. }
  102.  
  103.         begin
  104.             p^ := b;
  105.             p := Ptr(ord4(p)+1);
  106.         end;
  107.  
  108.     function linesEqual(x,y: integer): boolean;
  109.         { Compare two lines in the linePtrs array for equality. }
  110.  
  111.         var l1, l2: Str255;
  112.  
  113.         begin
  114.             ZeroToPas(paramPtr,linePtrs^^[x].compareStart,l1);
  115.             ZeroToPas(paramPtr,linePtrs^^[y].compareStart,l2);
  116.             linesEqual := StringEqual(paramPtr,l1,l2);
  117.         end;
  118.  
  119.     function nextCheckItem: boolean;
  120.  
  121.         function nextCheckNum: LongInt;
  122.     
  123.             var theResult: LongInt;
  124.     
  125.             begin
  126.                 theResult := 0;
  127.                 while (checkPtr^ >= SignedByte('0')) and (checkPtr^ <= SignedByte('9')) do
  128.                     begin
  129.                         theResult := 10*theResult + checkPtr^ - signedByte('0');
  130.                         checkPtr := Ptr(ord4(checkPtr)+1);
  131.                     end;
  132.                 nextCheckNum := theResult;
  133.             end;
  134.     
  135.         function nextCheckElement: nextElement;
  136.     
  137.             begin
  138.                 nextCheckElement := newWord;
  139.                 while (checkPtr^ <> 0) and ((checkPtr^ < SignedByte('0')) or (checkPtr^ > SignedByte('9'))) do
  140.                     begin
  141.                         if checkPtr^ = SignedByte(',') then nextCheckElement := newItem;
  142.                         checkPtr := Ptr(ord4(checkPtr)+1);
  143.                     end;
  144.                 if checkPtr^ = 0 then nextCheckElement := endOfString;
  145.             end;
  146.  
  147.         begin
  148.             if nextCheckElement = endOfString then nextCheckItem := false
  149.             else
  150.                 begin
  151.                     nextCheckItem := true;
  152.                     loCheck := nextCheckNum;
  153.                     if nextCheckElement = newWord then
  154.                         begin
  155.                             highCheck := nextCheckNum;
  156.                             if not (nextCheckElement in [endOfString,newItem]) then disposAndFail('§§§ bad sparse array §§§');
  157.                         end
  158.                     else highCheck := loCheck;
  159.                 end;
  160.         end;
  161.  
  162.     begin
  163.         if paramPtr^.paramCount <> 3 then Fail('§§§ parameter count is not 3 §§§');
  164.  
  165.         sourceHand := paramPtr^.params[1];                        { First parameter is the string to be grouped. }
  166.         ZeroToPas(paramPtr,paramPtr^.params[2]^,str);        { Second parameter is whether to group. }
  167.         groupMessages := StringEqual(paramPtr,str,'true');
  168.  
  169.         { If there's anything in the string, process it. }
  170.         if sourceHand <> NIL then
  171.             begin
  172.  
  173.                 { Create the line pointer array. }
  174.                 linePtrs := lineRecHand(NewHandle(0));
  175.                 if MemError <> noErr then Fail('§§§ NewHandle failed §§§');
  176.                 { Lock the source. }
  177.                 HLock(sourceHand);
  178.                 p := sourceHand^;
  179.                 { Find all the lines. }
  180.                 lineCount := 0;
  181.                 while p^ <> 0 do
  182.                     begin
  183.                         if lineCount >= 30000 then disposAndFail('§§§ too many lines §§§');
  184.                         { Fill it in. }
  185.                         with newEntry do
  186.                             begin
  187.                                 { Find the message number. }
  188.                                 numberStart := p;
  189.                                 while (p^ >= SignedByte('0')) and (p^ <= SignedByte('9')) do p := Ptr(ord4(p)+1);
  190.                                 if p^ = SignedByte(' ') then
  191.                                     begin
  192.                                         p^ := 0;
  193.                                         p := Ptr(ord4(p)+1);
  194.                                         while p^ = SignedByte(' ') do p := Ptr(ord4(p)+1);
  195.                                     end;
  196.                                 ZeroToPas(paramPtr,numberStart,str);
  197.                                 msgNumber := StrToNum(paramPtr,str);
  198.                                 { Find the text. }
  199.                                 textStart := p;
  200.                                 { Find the part of the text to compare (skip "re: " if it's there). }
  201.                                 compareStart := p;
  202.                                 checkMark := false;
  203.                                 indent := false;
  204.                                 if (p^ = SignedByte('R')) or (p^ = SignedByte('r')) then
  205.                                     begin
  206.                                         p := Ptr(ord4(p)+1);
  207.                                         if (p^ = SignedByte('E')) or (p^ = SignedByte('e')) then
  208.                                             begin
  209.                                                 p := Ptr(ord4(p)+1);
  210.                                                 if p^ = SignedByte(':') then
  211.                                                     begin
  212.                                                         p := Ptr(ord4(p)+1);
  213.                                                         while p^ = SignedByte(' ') do p := Ptr(ord4(p)+1);
  214.                                                         compareStart := p;
  215.                                                     end;
  216.                                             end;
  217.                                     end;
  218.                             end;
  219.                         { Skip to the end of the line. }
  220.                         while (p^ <> 0) and (p^ <> RETURN) do p := Ptr(ord4(p)+1);
  221.                         if p^ <> 0 then
  222.                             begin
  223.                                 p^ := 0;
  224.                                 p := Ptr(ord4(p)+1);
  225.                             end;
  226.                         { Find where it goes. }
  227.                         i := 1;
  228.                         dupEntry := false;
  229.                         while i <= lineCount do
  230.                             with linePtrs^^[i] do
  231.                                 if msgNumber = newEntry.msgNumber then
  232.                                     begin
  233.                                         dupEntry := true;
  234.                                         leave;
  235.                                     end
  236.                                 else if msgNumber > newEntry.msgNumber then leave
  237.                                 else i := i+1;
  238.                         { If this is not a duplicate entry, insert it. }
  239.                         { Note: Eliminating duplicates ensures we don't get snagged by an old NNTP server bug. }
  240.                         if not dupEntry then
  241.                             begin
  242.                                 { Allocate a line entry. }
  243.                                 lineCount := lineCount+1;
  244.                                 SetHandleSize(Handle(linePtrs),lineCount*sizeOf(lineRec));
  245.                                 if MemError <> noErr then disposAndFail('§§§ SetHandleSize failed §§§');
  246.                                 if i < lineCount then BlockMove(@linePtrs^^[i],@linePtrs^^[i+1],(lineCount-i)*sizeOf(lineRec));
  247.                                 linePtrs^^[i] := newEntry;
  248.                             end;
  249.                     end;
  250.  
  251.                 { Go through the check messages list and check the messages. }
  252.                 checkPtr := paramPtr^.params[3]^;
  253.                 i := 1;
  254.                 while nextCheckItem and (i <= lineCount) do
  255.                     begin
  256.                         while i <= lineCount do
  257.                             begin
  258.                                 if linePtrs^^[i].msgNumber >= loCheck then leave;
  259.                                 i := i+1;
  260.                             end;
  261.                         while i <= lineCount do
  262.                             begin
  263.                                 if linePtrs^^[i].msgNumber <= highCheck then linePtrs^^[i].checkMark := true
  264.                                 else leave;
  265.                                 i := i+1;
  266.                             end;
  267.                     end;
  268.  
  269.                 { Group the lines. }
  270.                 if groupMessages then
  271.                     begin
  272.                         { Cycle through them. }
  273.                         i := 1;
  274.                         while i < lineCount do
  275.                             begin
  276.                                 { Cycle through all possible matches. }
  277.                                 delta := 1;
  278.                                 j := i+1;
  279.                                 while j <= lineCount do
  280.                                     begin
  281.                                         { Is this one groupable? }
  282.                                         if linesEqual(i,j) then
  283.                                             begin
  284.                                                 { If it is, shift up the other lines, and move that one into place. }
  285.                                                 linePtrs^^[j].indent := true;
  286.                                                 if j > (i+1) then
  287.                                                     begin
  288.                                                         tempLine := linePtrs^^[j];
  289.                                                         for k := j downTo i+delta+1 do linePtrs^^[k] := linePtrs^^[k-1];
  290.                                                         linePtrs^^[i+delta] := tempLine;
  291.                                                     end;
  292.                                                 delta := delta+1;
  293.                                             end;
  294.                                         j := j+1;
  295.                                     end;
  296.                                 i := i+delta;
  297.                             end;
  298.                     end;
  299.  
  300.                 { Generate the output.}
  301.                 if lineCount > 0 then
  302.                     begin
  303.                         { First figure out how big the output is going to be. }
  304.                         resultSize := 0;
  305.                         for i := 1 to lineCount do
  306.                             with linePtrs^^[i] do
  307.                                 begin
  308.                                     if checkMark then resultSize := resultSize+1
  309.                                     else resultSize := resultSize+3;
  310.                                     if indent then resultSize := resultSize+2;
  311.                                     p := numberStart;
  312.                                     while p^ <> 0 do
  313.                                         begin
  314.                                             resultSize := resultSize+1;
  315.                                             p := Ptr(ord4(p)+1);
  316.                                         end;
  317.                                     resultSize := resultSize+3;
  318.                                     p := textStart;
  319.                                     while p^ <> 0 do
  320.                                         begin
  321.                                             resultSize := resultSize+1;
  322.                                             p := Ptr(ord4(p)+1);
  323.                                         end;
  324.                                     resultSize := resultSize+1;
  325.                                 end;
  326.                         { Allocate the result handle. }
  327.                         resultHand := NewHandle(resultSize);
  328.                         if MemError <> noErr then disposAndFail('§§§ NewHandle failed §§§');
  329.                         { Output the lines. }
  330.                         p := resultHand^;
  331.                         for i := 1 to lineCount do
  332.                             with linePtrs^^[i] do
  333.                                 begin
  334.                                     { Do the checkmark (if any). }
  335.                                     if checkMark then putByte(SignedByte('√'))
  336.                                     else
  337.                                         begin
  338.                                             putByte(SignedByte(' ')); putByte(SignedByte(' ')); putByte(SignedByte(' '));
  339.                                         end;
  340.                                     { Do the indentation (if any). }
  341.                                     if indent then
  342.                                         begin
  343.                                             putByte(SignedByte('…')); putByte(SignedByte('…'));
  344.                                         end;
  345.                                     { Output the subject text. }
  346.                                     p2 := textStart;
  347.                                     while p2^ <> 0 do
  348.                                         begin
  349.                                             putByte(p2^);
  350.                                             p2 := Ptr(ord4(p2)+1);
  351.                                         end;
  352.                                     { Append the message number. }
  353.                                     putByte(SignedByte(' ')); putByte(SignedByte('«'));
  354.                                     p2 := numberStart;
  355.                                     while p2^ <> 0 do
  356.                                         begin
  357.                                             putByte(p2^);
  358.                                             p2 := Ptr(ord4(p2)+1);
  359.                                         end;
  360.                                     putByte(SignedByte('»'));
  361.                                     putByte(RETURN);
  362.                                 end;
  363.                         { Zero terminate the result string. }
  364.                         p := Ptr(ord4(p)-1);
  365.                         p^ := 0;
  366.                         { Unlock the source. }
  367.                         HUnlock(sourceHand);
  368.                         { Dispose the line pointer array. }
  369.                         disposHandle(Handle(linePtrs));
  370.                         { Return the handle. }
  371.                         paramPtr^.returnValue := resultHand;
  372.                     end
  373.                 else disposAndFail('');
  374.             end
  375.         else Fail('');
  376.     end;
  377.  
  378. end.
  379.